package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.zzyl.constant.AdminCacheConstant;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.ResourceDto;
import com.zzyl.entity.Resource;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.service.ResourceService;
import com.zzyl.utils.EmptyUtil;
import com.zzyl.utils.NoProcessing;
import com.zzyl.utils.StringUtils;
import com.zzyl.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;

    @Override
    @Cacheable(value = AdminCacheConstant.RESOURCE_LIST, key = "#resourceDto.hashCode()")
    public List<ResourceVo> findResourceList(ResourceDto resourceDto) {
        List<Resource> resourceList = resourceMapper.selectResourceList(resourceDto);
        return BeanUtil.copyToList(resourceList, ResourceVo.class);
    }

    @Cacheable(value = AdminCacheConstant.RESOURCE_TREE)
    @Override
    public TreeVo resourceTreeVo(ResourceDto resourceDto) {
        //获取所有的资源的树结构
        //简化写法，链式编程
        ResourceDto build = ResourceDto.builder().build();
        //将资源列表转成树形结构
        List<Resource> resourceList = resourceMapper.selectResourceList(build);
        log.info("资源数量:{}", resourceList.size());

        //判断资源列表是否为空
        if (resourceList == null || resourceList.size() == 0) {
            throw new RuntimeException("资源信息未定义！！");
        }
        //至此父节点的所有菜单都查出来了，接下来，要将这个列表转成Tree
        //所有的数据出来了，发现100001000000000是权限模块的顶级节点
        Resource root = new Resource();
        root.setResourceNo(SuperConstant.ROOT_PARENT_ID);
        root.setResourceName("智慧养老");

        //按照root根节点信息，将上面的resourceList 转成items
        // resourceList只是一个列表，数据和数据之间关系层级不明显
        // 转成items后，出现俄罗斯套娃，嵌套关系明显，就能实现树级结构
        List<TreeItemVo> itemsvo = new ArrayList<>();

        recursionTreeItem(itemsvo, root, resourceList);

        TreeVo treeVo = new TreeVo(itemsvo);

        return treeVo;
    }

    @Override
    @Caching(
            evict = {
                    @CacheEvict(cacheNames = AdminCacheConstant.RESOURCE_TREE, allEntries = true),
                    @CacheEvict(cacheNames = AdminCacheConstant.RESOURCE_LIST, allEntries = true)
            }
    )
    public Boolean createResource(ResourceDto resourceDto) {
        // 1.将dto转成实体
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);
        //资源有禁用和启用，如果父资源是禁用，那么子资源也应该禁用

        //通过父资源编号，查询父资源，获得子编号
        Resource parentResource = resourceMapper.selectParentByNo(resource.getParentResourceNo());

        //资源类型：s平台 c目录 m菜单 r按钮"
        //如果是按钮，则不允许添加子节点
        if (parentResource.getResourceType().equals("r")) {
            throw new BaseException(BasicEnum.BUTTON_NOT_HAVE_CHILD);
        }

        //给实体类添加状态 —>父资源状态
        resource.setDataState(parentResource.getDataState());

        boolean isIgnore = true; //如果是按钮，就false
        // 资源的层级，支持3级

        //如果资源类型是空的，并且类型是按钮 则isIgnore = false;
        if (StringUtils.isNotEmpty(resourceDto.getResourceType())
                && resourceDto.getResourceType().equals(SuperConstant.BUTTON)) {
            isIgnore = false;
        }

        //通过父级编号，和按钮， 获得资源编号方法，
        String resourceNo = createResourceNo(resource.getParentResourceNo(), isIgnore);

        //将编号放入进实体类
        resource.setResourceNo(resourceNo);

        resourceMapper.insert(resource);
        return true;
    }

    @Override
    @Caching(
            evict = {
                    @CacheEvict(cacheNames = AdminCacheConstant.RESOURCE_TREE, allEntries = true),
                    @CacheEvict(cacheNames = AdminCacheConstant.RESOURCE_LIST, allEntries = true)
            }
    )
    public void updateResource(ResourceDto resourceDto) {
        //转换
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);
        int flag = resourceMapper.updateByPrimaryKeySelective(resource);
        if (flag == 0) {
            throw new RuntimeException("修改资源信息出错");
        }
    }

    /**
     * 启用禁用
     *
     * @param resourceVo
     * @return
     */
    @Override
    @Caching(
            evict = {
                    @CacheEvict(cacheNames = AdminCacheConstant.RESOURCE_TREE, allEntries = true),
                    @CacheEvict(cacheNames = AdminCacheConstant.RESOURCE_LIST, allEntries = true)
            }
    )
    public void isEnable(ResourceVo resourceVo) {
        //启用菜单
        if (resourceVo.getDataState().equals("0")) {
            //判断父级菜单是否是禁用，如果是禁用，不允许启用
            String parentResourceNo = resourceVo.getParentResourceNo();
            Resource parentResource = resourceMapper.selectByResourceNo(parentResourceNo);
            if (parentResource != null && parentResource.getDataState().equals("1")) {
                throw new BaseException(BasicEnum.PARENT_MENU_DISABLE);
            }
        }
        //TODO 当按钮被分配的时候，出错误提示：按钮已分配，不允许禁用

        //判断是否有子菜单，如果有子菜单，则一起启用或禁用
        String resourceNo = resourceVo.getResourceNo();
        resourceMapper.updateByResourceNo(resourceNo, resourceVo.getDataState());
        resourceMapper.updateByParentResourceNo(NoProcessing.processString(resourceNo), resourceVo.getDataState());
    }

    /**
     * 删除菜单
     *
     * @param resourceNo
     */
    @Override
    @Caching(
            evict = {
                    @CacheEvict(cacheNames = AdminCacheConstant.RESOURCE_TREE, allEntries = true),
                    @CacheEvict(cacheNames = AdminCacheConstant.RESOURCE_LIST, allEntries = true)
            }
    )
    public void deleteByResourceNo(String resourceNo) {
        if (hasChildByMenuId(resourceNo)) {
            throw new RuntimeException("存在子菜单,不允许删除");
        }

        Resource resource = resourceMapper.selectByResourceNo(resourceNo);
        if (resource == null || resource.getDataState().equals(SuperConstant.DATA_STATE_0)) {
            throw new RuntimeException("菜单启用状态,不能删除");
        }

        resourceMapper.deleteByResourceNo(resourceNo);
    }

    @Override
    public List<MenuVo> menus(Long userId) {
        //查询用户对应的所有的菜单数据
        List<MenuVo> menuVoList = resourceMapper.findListByUserId(userId);

        if (CollUtil.isEmpty(menuVoList)) {
            throw new BaseException(BasicEnum.USER_ROLE_AND_MENU_EMPTY);
        }

        //数据进行分组{parentNo：[{},{}]
        Map<String, List<MenuVo>> parentRNoMap = menuVoList.stream().collect(Collectors.groupingBy(MenuVo::getParentResourceNo));

        //遍历所有数据
        menuVoList.forEach(menuVo -> {
            menuVo.setMeta(MenuMetaVo.builder().title(menuVo.getName()).build());
            menuVo.setRedirect("/" + menuVo.getName());

            //根据父编号到map中查找是否包含子菜单，如果包含则设置为当前菜单的子菜单
            List<MenuVo> menuVos = parentRNoMap.get(menuVo.getResourceNo());

            if (!EmptyUtil.isNullOrEmpty(menuVos)) {

                menuVos.forEach(m -> {
                    m.setMeta(MenuMetaVo.builder().title(m.getName()).build());
                    m.setRedirect("/"+ menuVo.getName()+"/"+ m.getName());
                });

                menuVo.setChildren(menuVos);
            }
        });

        //根据根编号查找所有的子
        return parentRNoMap.get(SuperConstant.ROOT_PARENT_ID);
    }

    /**
     * 判断是否有子菜单
     *
     * @param resourceNo
     * @return
     */
    private boolean hasChildByMenuId(String resourceNo) {
        int result = resourceMapper.hasChildByMenuId(resourceNo);
        return result > 0 ? true : false;
    }

    private String createResourceNo(String parentResourceNo, boolean isIgnore) {
        // 菜单不能大于三级，可以通过父亲的编号来判断，如果父亲已经是三级了，就不能再加了
        //* 平台 100 000 000 000 000-->100--1
        //* 第1层：100 001 000 000 000-->100 001---2
        //* 第2层：100 001 001 000 000-->100 001 001--3
        //* 第3层：100 001 001 001 000-->100 001 001 001--4
        //* 第4层：100 001 001 001 001 001-->100 001 001 001 001--5

        //如果是按钮，并且是三级，则不能再加了
        if (isIgnore && (NoProcessing.processString(parentResourceNo).length() / 3) >= 5) {
            throw new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
        }
        //现在有了父亲的编号
        //转换为Dto
        ResourceDto parentResourceDto = ResourceDto.builder().parentResourceNo(parentResourceNo).build();

        //通过查询所有返回一个子级的资源编号集合
        List<Resource> childrenReresourcesList = resourceMapper.selectResourceList(parentResourceDto);

        //判断子级编号集合是否是空的
        if (EmptyUtil.isNullOrEmpty(childrenReresourcesList)) {
            // 如果父亲没有儿子，我就是老大
            return NoProcessing.createNo(parentResourceNo, false);

        } else {
            // 有儿子，找到儿子中，最大的编号
            Long maxNo = childrenReresourcesList.stream().map(item -> {
                return Long.valueOf(item.getResourceNo());
            }).max(Comparator.comparing(i -> i)).get();

            return NoProcessing.createNo(String.valueOf(maxNo), true);
        }

    }

    /**
     * 递归：将列表转成树结构
     *
     * @param itemsvo      树的容器
     * @param root         根节点
     * @param resourceList 所有的资源
     */
    private void recursionTreeItem(List<TreeItemVo> itemsvo, Resource root, List<Resource> resourceList) {

        TreeItemVo rootVo = TreeItemVo.builder()
                .id(root.getResourceNo())
                .label(root.getResourceName())
                .build();
        // 将所有资源遍历，
        List<Resource> rootChildVo = resourceList.stream()
                .filter(resource -> resource.getParentResourceNo().equals(root.getResourceNo()))
                .collect(Collectors.toList());

        //判断儿子s是否是空的
        if (!EmptyUtil.isNullOrEmpty(rootChildVo)) {
            List<TreeItemVo> childList = new ArrayList<>();
            //递归
            rootChildVo.forEach(item -> {
                recursionTreeItem(childList, item, resourceList);
            });
            rootVo.setChildren(childList);
        }
        itemsvo.add(rootVo);

    }
}
